// 手写promise
/**
1.Promise 是一个类, 在执行这个类的时候 需要传递一个执行器进去,执行器会立即执行
2.Promise 中有三种状态，成功=>fulfilled，失败=>rejected，等待=>pending
    pending -->fulfilled
    pending --> rejected
    一旦状态确定就不可更改
3.resolve和reject函数是用来更改状态的
    resolve: fulfilled
    reject: rejected
4.then方法内部做的事情就是判断状态,如果状态时成功,调用成功的回调函数,如果状态是失败,调用失败的回调函数,then方法是被定义在原型对象中的方法
5.then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
6.同一个promise对象下面的then方法是可以被调用多次的
7.then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
8.catch只是then的一种特殊的写法 方便理解和使用
9.Promise.all 特点  1. 返回一个Promise
                   2. 入参是数组 resolve的情况下出参也是数组 且结果顺序和调用顺序一致
                   3. 所有的值或者promise都完成才能resolve 所有要计数
                   4. 只要有一个为reject 返回的Promise便reject
10. Promise.race 特点 1. 返回一个Promise
                   2. 入参是数组 那么出参根据第一个成功或者失败的参数来确定
                   3. 只要有一个resolve 或者reject 便更改返回Promise的状态
*/

// 状态定义成常量代码会有提示并且方便复用
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
    constructor(executor) {
        // 立即执行传入参数
        // 异步执行状态变更
        // 捕获执行器的异常
        try {
            executor(
                (value) => {
                    queueMicrotask(() => {
                        this.resolve(value);
                    });
                },
                (reason) => {
                    queueMicrotask(() => {
                        this.reject(reason);
                    });
                }
            );
        } catch (error) {
            this.reject(error);
        }
    }
    // promise 状态
    status = PENDING;
    // 成功之后的值
    value = undefined;
    // 失败后的原因
    reason = undefined;
    // 成功回调
    successCallback = [];
    // 失败回调
    failCallback = [];
    // all是静态方法
    static all(array) {
        let result = [];
        let index = 0;
        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value;
                index++;
                // 因为for循环会很快结束,这一步代码主要是等待异步操作完成之后才resolve
                if (index === array.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    // promise对象
                    current.then(
                        (value) => {
                            addData(i, value);
                        },
                        (reason) => {
                            reject(reason);
                        }
                    );
                } else {
                    // 普通值
                    addData(i, current);
                }
            }
        });
    }
    // resolve是静态方法,作用是将传入参数转变为promise对象
    static resolve(value) {
        // 如果是promise对象则直接返回
        if (value instanceof MyPromise) return value;
        // 如果是普通值则创建一个promise对象返回
        return new MyPromise((resolve) => resolve(value));
    }
    // race 是静态方法 只要有一个成功或者失败就返回
    static race(array) {
        return new Promise((resolve, reject) => {
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                // 如果是MyPromise实例
                if (current instanceof MyPromise) {
                    current.then(resolve, reject);
                } else {
                    // 普通值
                    resolve(current);
                }
            }
        });
    }
    // 箭头函数目的是为了让this指向类的实例对象
    resolve = (value) => {
        // 如果状态不是等待,阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值
        this.value = value;
        // 判断成功回调是否存在,如果存在调用,成功的值必须传入
        while (this.successCallback.length) {
            this.successCallback.shift()();
        }
    };
    reject = (reason) => {
        // 如果状态不是等待,阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败之后的原因
        this.reason = reason;
        // 判断失败回调是否存在,如果存在调用,失败的原因必须传入
        while (this.failCallback.length) {
            this.failCallback.shift()();
        }
    };
    then(successCallback, failCallback) {
        // 如果then方法中没有传入函数,那么需要将值一直向后传递,一直传递给有回调函数的then方法
        successCallback =
            typeof successCallback === "function"
                ? successCallback
                : (value) => value;
        failCallback =
            typeof failCallback === "function"
                ? failCallback
                : (reason) => {
                      // 抛出一个reason表达式的异常
                      throw reason;
                  };

        // 实现then方法的链式调用,必须要让then方法自身返回一个promise对象
        let promise2 = new MyPromise((resolve, reject) => {
            // 统一异常处理逻辑
            const execFun = (fn, val) => {
                try {
                    let x = fn(val);
                    // 判断x的值是普通值还是promise对象
                    // 1.如果是普通值 直接调用resolve
                    // 2.如果是promise对象,查看promise对象的返回结果,再根据promise对象返回结果 决定调用resolve还是调用reject
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    // 调用下一个promise的reject方法,将错误信息传入
                    reject(e);
                }
            };
            // 判断状态
            if (this.status === FULFILLED) {
                // 内部无法直接拿到promise,所以需要将这一步操作变成异步操作,拿到promise2之后再调用
                queueMicrotask(() => {
                    execFun(successCallback, this.value);
                });
            } else if (this.status === REJECTED) {
                queueMicrotask(() => {
                    execFun(failCallback, this.reason);
                });
            } else {
                // 同步将对应成功或者失败回调事件加入对应回调队列, then可以多个处理所以是个数组,push到数组中一个函数,函数内部进行异步操作来调用相对应的成功/失败的回调
                this.successCallback.push(() => {
                    execFun(successCallback, this.value);
                });
                this.failCallback.push(() => {
                    execFun(failCallback, this.reason);
                });
            }
        });
        return promise2;
    }
    // finally无论成功还是失败callback都会执行,并且返回当前promise对象最终返回的结果
    finally(callback) {
        return this.then(
            (value) => {
                return MyPromise.resolve(callback()).then(() => value);
            },
            (reason) => {
                return MyPromise.resolve(callback()).then(() => {
                    throw reason;
                });
            }
        );
    }
    // catch 方法用来处理当前promise对象最终状态为失败的情况(直接调用then方法即可)
    catch(failCallback) {
        // 内部也是调用的then方法,只不过没有成功的回调
        return this.then(undefined, failCallback);
    }
}

// 定义一个判断成功回调值的类型
function resolvePromise(promise2, x, resolve, reject) {
    // 如果then方法返回的promise和成功的回调返回的promise是同一个对象则程序报出类型错误并且不再向下进行
    if (promise2 === x) {
        return reject(
            new TypeError("Chaining cycle detected for promise #<Promise>")
        );
    }
    // 判断如果是MyPromise的实例则调用then方法,根据传入的参数来判断是执行resolve还是reject
    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else {
        // 普通值
        resolve(x);
    }
}

// let promise = new MyPromise((resolve, reject) => {
//     setTimeout(() => {
//         resolve("成功......");
//     }, 2000);
// });
function other() {
    return new MyPromise((resolve, reject) => {
        resolve("other");
    });
}

// promise
//     .then(
//         (res) => {
//             console.log(res);
//             return "aaa";
//         },
//         (err) => {
//             console.log(err);
//         }
//     )
//     .then(
//         (res) => {
//             console.log(res);
//         },
//         (err) => {
//             console.log(err);
//         }
//     );

// let promise = new MyPromise((resolve, reject) => {
//     // resolve(100);
//     reject("失败");
// });
// promise
//     .then((value) => value)
//     .then((value) => value)
//     .then(
//         (res) => console.log(res),
//         (err) => {
//             console.log(err);
//         }
//     );
function p1() {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve("p1");
        }, 2000);
    });
}
function p2() {
    return new MyPromise((resolve, reject) => {
        reject("p2,reject");
    });
}

// MyPromise.all(["a", "b", p1(), p2(), "c"])
//     .then((res) => {
//         console.log(res);
//     })
//     .catch((err) => {
//         console.log(err);
//     });

// MyPromise.resolve(10).then((res) => console.log(res));
// MyPromise.resolve(p1()).then((res) => console.log(res));

// p2()
//     .finally(() => {
//         console.log("finally");
//         return p1();
//     })
//     .then(
//         (res) => console.log(res, "res"),
//         (err) => console.log(err, "err")
//     )
//     .catch((err) => {
//         console.log(err, 111);
//     });

// MyPromise.race([p2(), p1(), "a", "b", "c"])
//     .then((res) => {
//         console.log(res, 1);
//     })
//     .catch((err) => {
//         console.log(err, 2);
//     });

Promise.race([p2(), p1(), "a", "b", "c"])
    .then((res) => {
        console.log(res, 1);
    })
    .catch((err) => {
        console.log(err, 2);
    });
